tree grammar CodeChecker;
// [file: Checker.g, started: 04-06-2012]
// Basic Expression Language
// CodeChecker.g: Checker
// @author   Keith Davelaar
// @version  2012.06.24

options {
    language = Java;
    output = AST;
    tokenVocab=Compiler;               // Import tokens from Superhero.tokens
    ASTLabelType=CommonTree;            // AST nodes are of type CommonTree
}

@header{
	import entries.*;
}

@rulecatch { 
    catch (RecognitionException e) { 
        exceptions.add(e); 
    } 
}

@members {
    //symbols - a set of declared identifiers.
    private SymbolTable symbols = new SymbolTable();
    
    //For error handling
    public List<RecognitionException> exceptions = new ArrayList<RecognitionException>();
}

// {{{---Checker rules---}}} \\
program
    :	^(BEGIN statement+)
    ;

statement returns [char type]
    :	dec=declaration {$type = Entry.NONE;}
    |	pr=procedure {$type = Entry.NONE;}
    |	fc=function {$type = Entry.NONE;}
    |	ex=expression {$type = ex.type;}
    ;

declaration
    :	^(CD1 id=IDENTIFIER op=operand)
        	{
        		symbols.constantDeclaration(id, $id.text, $op.type);
        	}
    |	^(VD1 id=IDENTIFIER tp=(INTDEF|CHARDEF|BOOLDEF)) 
        	{
        		symbols.variableDeclaration(id, $id.text, $tp.text);
       		}
    ;
    
procedure
	:	^(pr=PROC1 id=IDENTIFIER
		{
			int pars = 0;
			symbols.openScope();
			symbols.procedureCreation(pr, $id.text);
		}
		(par=parameter 
		{
			symbols.parameterCreation(pr, $id.text, $par.id, $par.type, pars++);
		}
		)* p_compound 
		{
			symbols.closeScope();
		})
	;
	
p_compound
	:	^(LCURLY (declaration|expression)+)
	;

function
	:	^(fun=FUNC1 id=IDENTIFIER
		{
			int pars = 0;
			symbols.openScope();
			symbols.functionCreation(fun, $id.text);
		}
		(par=parameter
		{
			symbols.parameterCreation(fun, $id.text, $par.id, $par.type, pars++);
		}
		)* ret=(INTDEF|CHARDEF|BOOLDEF) fc=f_compound
		{
			symbols.checkFunction(fc, $id.text, $ret.text);
			symbols.closeScope();
		})
	;
	
f_compound returns [char retType]
    : 	^(LCURLY (declaration|expression)* RETURN ex=expression)
    	{ 
    		$retType = $ex.type; 
    	}
    ;

parameter returns [String id, String type]
	:	identifier=IDENTIFIER typedef=(INTDEF|CHARDEF|BOOLDEF)
		{
			$id = $identifier.text;
			$type = $typedef.text;
		}
	;
			
expression returns [char type]
    :	ae=assignment {$type=ae.type;}
    |	arith=arithmetic {$type=arith.type;}
    ;

assignment returns [char type]
    :	^(ae=(AE1|AE2) id=IDENTIFIER ex=expression)
        	{ 
        		symbols.assign(ae, $id.text);
        		TypeChecker.checkAssignment(ae, symbols.getType($id.text), ex);
        		$type = symbols.getType($id.text);
        	}
    ;
    
arithmetic returns [char type]
    :	^(sign=(OR|AND|LT|LE|GE|GT|EQ|NE|MULT|DIV|MOD) ex1=expression ex2=expression)
        {
          $type=TypeChecker.checkArithmetic(sign, $sign.text, ex1, ex2);
        }
    |	^(sign=(PLUS|MINUS|NOT) (ex1=expression (ex2=expression)?))
        {
        	if(ex2==null){
        		$type = $ex1.type;
        	}else{
        		$type = TypeChecker.checkArithmetic(sign, $sign.text, ex1, ex2);
        	}
        }
    |	op=operand { $type=$op.type; }
    ;    
    
operand returns [char type]
    : 	id=IDENTIFIER
        	{  
	        	symbols.isDeclared(id, $id.text);
	        	$type=symbols.getType($id.text);
	        	symbols.isAssigned(id, $id.text);
        	}
    |	INTEGER {$type=Entry.INTEGER;}
    | 	CHARACTER {$type=Entry.CHARACTER;}
    |	TRUE {$type=Entry.BOOLEAN;}
    | 	FALSE {$type=Entry.BOOLEAN;}
    | 	readex=read {$type=readex.type;}
    | 	pe=print {$type=pe.type;}
	|	{ symbols.openScope(); } ce=compound { $type=ce.type; symbols.closeScope(); }
    | 	ie=conditional {$type=ie.type;}
    | 	we=dowhile {$type=we.type;}
    |	pc=procCall {$type=pc.type;}
	|	fc=funcCall {$type=fc.type;}
    ;

compound returns [char type]
    : 	^(ce=(LCURLY|CE1) (stat=statement)+)
        	{
        		$type = TypeChecker.checkCompound(ce, stat);
        	}
    ;
        
read returns [char type]
    :	^(readex=RE {int ids = 0;} (id=IDENTIFIER {symbols.assign(readex, $id.text); ids++;} )+
    		{
    			if(ids == 1){
    				$type = symbols.getType($id.text);
    			}else{
    				$type = Entry.VOID;
    			}
    		})
    ;
    
print returns [char type]
    :	^(pe=PE1 (expr=expression
    	{	
    		if($expr.type == Entry.VOID) 
    		throw new CompilerException($expr.tree, "Expression is of type void, which isn't allowed");
    	}
    	)+)
        {
          	if($pe.getChildCount() == 1){
    			$type = $expr.type;
    		}else{
    			$type = Entry.VOID;
    		}
        }
    ;
    
conditional returns [char type]
	:	^(ie=IE 
		{ symbols.openScope(); } 
		ce=compound
		{ symbols.openScope(); } 
		ce1=compound 
		{ symbols.closeScope(); symbols.openScope(); }
		ce2=compound? 
		{ symbols.closeScope(); symbols.closeScope(); }
		)
		{ $type = TypeChecker.checkConditional(ie, ce, ce1, ce2); }
  	;

dowhile returns [char type = Entry.VOID]
    :	^(we=WE {symbols.openScope();} ce=compound {symbols.openScope();} compound {symbols.closeScope();} {symbols.closeScope();})
        {
          TypeChecker.checkDowhile(we, ce);
        }
    ;

procCall returns [char type=Entry.NONE]
	:	^(pc=PC 
		{
			List<Character> types = new ArrayList<Character>();
		}
		id=IDENTIFIER { symbols.checkCall(pc, $id.text); }
		(expr=expression {types.add($expr.type);})* 
		{symbols.checkParameter(pc, $id.text, types);} )
	;
	
funcCall returns [char type]
	:	^(fc=FC 
		{
			List<Character> types = new ArrayList<Character>();
		}
		id=IDENTIFIER { $type=symbols.checkCall(fc, $id.text); }
		(expr=expression {types.add($expr.type);})* 
		{symbols.checkParameter(fc, $id.text, types);} )
	;
//E{Checker.g}